#ifndef INC_COMMAND_H
#define INC_COMMAND_H

#include <string>
#include <vector>
#include <exception>
using std::string;
using std::vector;
using std::exception;

#include <Poco/Net/SocketAddress.h>
using Poco::UInt16;
using Poco::Net::SocketAddress;

#include <Poco/Semaphore.h>
using Poco::Semaphore;

#include <Poco/DOM/Document.h>
#include <Poco/DOM/DocumentFragment.h>
using Poco::XML::Document;

namespace Commands {

	class NotImplemented: public exception {};

	class Command;

	/**
	 * Can create a command using its representational string
	 * @return Command corresponding to the representational string
	 */
	typedef Command* (*CommandCreator)(string, void*);
	
	class Command
	{
		public:
			typedef unsigned long id_type;

			/**
			 * @param s Socket on which this command was received.
			 */
			Command(SocketAddress* s = 0, bool needs_result = false);

			virtual ~Command() {}

			/**
			 * Executes the command
			 */
			virtual void execute() = 0;

			/**
			 * Returns the name by which the command is registered with a CommandFactory
			 * @return commandname
			 */
			virtual string name() = 0;

			/**
			 * Undoes this command, if implemented. If not implemented, throws an NotImplemented excpetion.
			 */
			virtual void undo() { throw new NotImplemented(); }
			
			/**
			 * Return a string with which this command can be remade if it were fed into a CommandFactory
			 * @return representational string
			 */
			virtual string repr() = 0;

			/**
			 * Return the creator to make this command given the representational string
			 * @return a CommandCreator for this Command
			 */
			virtual CommandCreator creator() = 0;
			
			/**
			 */
			string result();
			
			/**
			 * Set the result of the command, with an optional originator address. Command ignores this.
			 */
			virtual void setResult(string res, SocketAddress const addr = SocketAddress());

			/**
			 * @return Remote hostname on which the command was received
			 */
			string hostname() const;

			/**
			 * @return Remote port on which the command was received
			 */
			UInt16 port() const;

			/**
			 * @return The ID of this command.
			 */
			id_type id() const;
			
			virtual bool isUnknown(){
				return false;
			}
		protected:
			/**
			 * Deriving Commands should call this method at the end of their toXML
			 */
			virtual Document* toXML(Document* fragment = 0);

			/**
			 * Constructor for creating a command with an ID
			 */
			Command(id_type id, SocketAddress* s = 0, bool needs_result = true);

		private:
			string const _remoteHost;
			UInt16 const _remotePort;
			string _result;
			Semaphore _resultSem;
			bool _resultSet;
			bool const _has_result;
			id_type _id;

			static id_type _newId;
			static id_type createNewId();
	};

}
#endif
